`timescale 1ns/1ps

/*
Created @20240919:
该模块发送触发信号SH_R至LM98725芯片（CCD驱动芯片），随即该芯片输出预先配置好的驱动

CCD驱动芯片输出的R2输入至该模块，该模块内部检测到R2的下降沿之后，使能reg_data_rdy信号(即之前版本中的wrdata_rdy)，
ADS5294_8CH模块看到REG_DATA_SAMPLE拉高后，开始对采集到的ADC数据进行DSP处理，并将处理结果写入PS端DDR。

行、列计数

*/

module MOSAIC_CCD(
    
    input trigger,              // 从PS端过来的触发信号(临时的)
    
    input ADC_CLK,              // ADS5294的frame clk (FCLK1 & FCLK2) 

    input clk_reg,
    
    // input FPGA_master,          // 由PS端GPIO口控制,为1时表示当前FPGA为"主模式",为0时表示当前FPGA为"从模式"
    
    input[15:0] vtrans_num,     // 垂直转移行数
    input[15:0] htrans_num,     // 水平转移次数
    
    input R2,                   // register clock R2
    output SH_R,

    output reg_data_rdy,        // 可以开始采集和处理像元数据的使能信号
    output [31:0] ddr_addr      // 数据写入DDR时所需的地址
);

//#############################################################################

    localparam INIT_ADDR    = 32'h04000000 - 32'b0100;  // 这是个特殊的初始位置，（实际左移3bit）对应内存地址512M
    reg[31:0] ddr_addr_r    = 32'h04123456;
    // localparam INIT_ADDR    = 32'd99;  // 这是个特殊的初始位置，（实际左移3bit）对应内存地址512M
    // reg[31:0] ddr_addr_r    = 32'd314;

    reg[15:0] vtrans_cnt    = 16'd0;       // 垂直转移计数器
    reg[15:0] htrans_cnt    = 16'd0;       // 水平转移计数器

//    wire readout_done = (vtrans_cnt >= vtrans_num) && (htrans_cnt >= htrans_num);
    wire readout_done = (vtrans_cnt > vtrans_num) && (htrans_cnt >= htrans_num);

    reg reg_data_rdy_r = 1'b0;  // 在R2下降沿时锁存为1,表示可以开始采集ADC数据
    
//    wire vtrans_cnt_is_max = (vtrans_cnt >= vtrans_num);
    wire vtrans_cnt_is_max = (vtrans_cnt > vtrans_num);
    wire htrans_cnt_is_max = (htrans_cnt >= htrans_num);

//#############################################################################
//  状态机

    reg[31:0] vt_cnt = 32'd0;
    localparam vt_cnt_max = 32'd16;

    // 状态定义
    localparam IDLE         = 4'd1;
    localparam S_HT_PRE     = 4'd2;
    localparam S_HT         = 4'd3;
    localparam S_HT_POS     = 4'd4;
    localparam S_VT_PRE     = 4'd5;
    localparam S_VT         = 4'd6;
    localparam S_VT_POS     = 4'd7;
    localparam S_SLEEP      = 4'd8;

    reg[3:0] state_c = S_SLEEP;
    reg[3:0] state_n;

    // 状态迁移条件
    wire idle2htpre;
    wire htpre2ht;
    wire ht2htpos;
    wire htpos2vtpre, htpos2sleep;
    wire vtpre2vt;
    wire vt2vtpos;

    always @(negedge clk_reg or posedge trigger) begin
        if (trigger) begin
            state_c <= IDLE;
        end
        else begin 
            state_c <= state_n;
        end
    end

    // 时序逻辑描述状态迁移条件判断
    always@(*) begin
        case( state_c )
            IDLE: begin
                if( idle2htpre ) begin
                    state_n = S_HT_PRE;
                end
                else begin
                    state_n = state_c;
                end
            end

            S_HT_PRE: begin
                if( htpre2ht ) begin
                    state_n = S_HT;
                end
                else begin
                    state_n = state_c;
                end
            end

            S_HT: begin
                if( ht2htpos ) begin
                    state_n = S_HT_POS;
                end
                else begin
                    state_n = state_c;
                end
            end

            S_HT_POS: begin
                if( htpos2vtpre) begin
                    state_n = S_VT_PRE;
                end
                else if( htpos2sleep ) begin
                    state_n = S_SLEEP;
                end
                else begin
                    state_n = state_c;
                end
            end

            S_VT_PRE: begin
                if( vtpre2vt ) begin
                    state_n = S_VT;
                end
                else begin
                    state_n = state_c;
                end
            end

            S_VT: begin
                if( vt2vtpos ) begin
                    state_n = S_VT_POS;
                end
                else begin
                    state_n = state_c;
                end
            end

            S_VT_POS: begin 
                if( vtpos2htpre ) begin
                    state_n = S_HT_PRE;
                end
                else begin
                    state_n = state_c;
                end
            end

            S_SLEEP: begin
                state_n = S_SLEEP;
            end

            default: begin
                state_n = S_SLEEP;
            end
        endcase
    end

    assign idle2htpre   = (state_c == IDLE);
    assign htpre2ht     = (state_c == S_HT_PRE);
    assign ht2htpos     = (state_c == S_HT) && (htrans_cnt >= htrans_num);
    
    assign htpos2vtpre  = (state_c == S_HT_POS) && (vtrans_cnt <= vtrans_num);
    assign htpos2sleep  = (state_c == S_HT_POS) && (vtrans_cnt > vtrans_num);

    assign vtpre2vt     = (state_c == S_VT_PRE);
    assign vt2vtpos     = (state_c == S_VT) && (vt_cnt >= vt_cnt_max);
    assign vtpos2htpre  = (state_c == S_VT_POS);

    always@( posedge clk_reg ) begin
        if( state_c == S_VT ) begin
            vt_cnt <= vt_cnt + 32'd1;
        end
        else begin
            vt_cnt <= 32'd0;
        end
    end

//#############################################################################
    
    // 水平转移计数器
    always@( posedge SH_R or negedge R2 ) begin
        if( SH_R == 1'b1 ) begin
            htrans_cnt  <= 16'd0;
        end
        else begin
            if( htrans_cnt_is_max == 1'b0 ) begin
                htrans_cnt <= htrans_cnt + 16'd1;
            end
            else if( htrans_cnt_is_max == 1'b1 ) begin
                htrans_cnt <= 16'd0;
            end
        end
    end

    // 垂直转移计数器
    always@( posedge trigger or posedge R2 ) begin
        if( trigger == 1'b1 ) begin
            vtrans_cnt  <= 16'd0;
        end
        else if( R2 == 1'b1 ) begin
            if( (htrans_cnt_is_max == 1'b1) && (vtrans_cnt_is_max == 1'b0) ) begin
                vtrans_cnt <= vtrans_cnt + 16'd1;
            end
        end
    end

    // DDR地址
    // always@( posedge trigger or posedge R2 ) begin
    always@( posedge trigger or negedge R2 ) begin
        if( trigger == 1'b1 ) begin
            ddr_addr_r  <= INIT_ADDR;
        end 
        else begin
            if( (vtrans_cnt > 16'd0) ) begin
                // ddr_addr_r <= ddr_addr_r + 32'd1;
                ddr_addr_r <= ddr_addr_r + 32'b0100;
            end
        end
    end

    // DCDS使能
    always@( posedge ADC_CLK ) begin
        if( readout_done == 1'b0 ) begin
            reg_data_rdy_r <= ~R2;
        end
    end
    
    assign SH_R             = state_c == S_HT_PRE;          // SH_R直接由PS端的GPIO生成
    assign reg_data_rdy     = reg_data_rdy_r;

    assign ddr_addr         = ddr_addr_r;

endmodule
